﻿using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;

namespace ANLP
{
    public delegate LexLocation LexerHandler(string input, int startAt);
    public delegate int TokenHandler();

    public abstract class LexerBase
    {
        public LexerBase()
        {
            lexExpressions = new Dictionary<int, IDictionary<LexerHandler, TokenHandler>>();
            Initialize();
        }

        public int Line { get { return Count(input.Substring(0, Position), '\n'); } }
        public int CharPosition { get { return Position - input.Substring(0, Position).LastIndexOf('\n'); } }

        private static int Count(string input, char charToCount)
        {
            int result = 0;
            int lastIndex = -1;
            while ((lastIndex = input.IndexOf(charToCount, lastIndex + 1)) >= 0)
                result++;
            return result;
        }

        protected static void AddRegex(IDictionary<LexerHandler, TokenHandler> expressions, string pattern, TokenHandler result)
        {
            Regex regex = new Regex(pattern, RegexOptions.Multiline);
            expressions.Add(delegate(string input, int startAt)
            {
                var match = regex.Match(input, startAt);
                if (match.Success)
                    return new LexLocation(match.Index, match.Length);
                else
                    return default(LexLocation);

            }, result);
        }

        public static void AddString(IDictionary<LexerHandler, TokenHandler> expressions, string pattern, TokenHandler result)
        {
            expressions.Add(delegate(string input, int startAt)
            {
                if (IndexOf(input, pattern, startAt) == startAt)
                    return new LexLocation(startAt, pattern.Length);
                else
                    return default(LexLocation);
            }, result);
        }

        private static int IndexOf(string input, string pattern, int startAt)
        {
            for (int i = 0; i < pattern.Length; i++)
            {
                if (i + startAt >= input.Length || input[i + startAt] != pattern[i])
                    return -1;
            }
            return startAt;
        }

        protected abstract void Initialize();

        private int currentState;

        public void ChangeState(int newState)
        {
            currentState = newState;
        }

        protected IDictionary<int, IDictionary<LexerHandler, TokenHandler>> lexExpressions;

        private string input;
        public void Read(string input)
        {
            this.input = input;
            endPosition = 0;
        }

        LexLocationComparer comparer = new LexLocationComparer();

        public int Next(int expectedToken)
        {
            if (endPosition >= input.Length)
                return -1;
            //IList<KeyValuePair<LexLocation, TokenHandler>> locations = new SortedList<LexLocation, TokenHandler>(new LexLocationComparer());
            startPosition = endPosition;
            //text = input.Substring(startPosition);
            LexLocation location = null;
            TokenHandler handler = null;
            foreach (var expressionStatement in lexExpressions[currentState])
            {
                LexLocation match = expressionStatement.Key(input, startPosition);
                if (match != null)
                {
                    if (location == null)
                    {
                        location = match;
                        handler = expressionStatement.Value;
                    }
                    else
                    {
                        if (comparer.Compare(location, match) >= 0)
                        {
                            location = match;
                            handler = expressionStatement.Value;
                        }

                    }
                }
            }
            if (location != null)
            {
                text = input.Substring(location.Start, location.End);
                endPosition = location.Start + location.End;
                return handler();
            }

            return -2;
        }

        protected int startPosition;
        protected int endPosition;
        protected string text;

        public string GetText()
        {
            return text;
        }

        public bool TryGet(out int result)
        {
            return int.TryParse(text, out result);
        }

        public bool TryGet(out bool result)
        {
            return bool.TryParse(text, out result);
        }
        public bool TryGet(out double result)
        {
            return double.TryParse(text, out result);
        }
        public bool TryGet(out DateTime result)
        {
            return DateTime.TryParse(text, out result);
        }


        public int Position
        {
            get { return endPosition; }
            set
            {
                if (endPosition < value)
                    throw new InvalidOperationException("You cannot go forward in a lexer");
                endPosition = value;
            }
        }
    }
}
